/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpr.inf.escel.brain.operators.util;

import br.ufpr.inf.escel.brain.model.po.DadoTestePO;
import br.ufpr.inf.escel.brain.model.po.Metodo;
import br.ufpr.inf.escel.brain.model.po.Metodos;
import br.ufpr.inf.escel.brain.model.po.Parametro;
import br.ufpr.inf.escel.brain.model.po.Referencias;
import br.ufpr.inf.escel.brain.model.po.Parametros;
import br.ufpr.inf.escel.brain.parsers.FormatarDadoTestePO;
import br.ufpr.inf.escel.consts.Constantes;
import java.util.List;

/**
 * Classe que realiza os operadores de mutação
 * para programas OO. São 4 tipos de operadores,
 * e estão descritos com o mesmo nome no trabalho
 * escrito. Alguns detalhes no cabeçalho de cada
 * método
 *
 * @author gustavo
 * @since 2.6
 */
public class MutacaoUtils {

    public static MutacaoUtils getInstance() {
        return new MutacaoUtils();
    }

    /**
     * Nessa mutação, um valor é sorteado e em seguida, é substituido
     * por um outro valor aleatório, sendo este do mesmo tipo
     * 
     * @param dt in
     * @return dt out modify
     * @since 2.6
     */
    public DadoTestePO valorEntrada(DadoTestePO dt) {

        Parametros parametros = dt.getParametros();

        //verifica se o dt tem parametros
        if (parametros.hasParameter()) {

            //sorteia o novo parametro
            Parametro parametro = parametros.sortearParametro();
            //modifica o parametro
            parametro = parametro.modificaTipoParametro();
            //devolve
            parametros.setParametro(parametro);
            //alteraana
            dt.setParametros(parametros);

            return dt;
        }

        return dt;
    }

    /**
     * uma nova invocação de método é inserida. Valores
     * ou parâmetros necessários para a invocação são inseridos.
     *
     * @param dt in
     * @return dt out
     */
    public DadoTestePO inserirInvocacaoMetodo(DadoTestePO dt) {

        Metodos metodos = dt.getMetodos();
        //tamanho maximo
        if (metodos.quantidade() > Constantes.TAMANHO_MAX_DT) {
            return dt;
        }
        Parametros parametros = dt.getParametros();

        List vetorMetodos = FormatarDadoTestePO.getVetorMetodos();
        int indiceMetodo = (int) (Math.random() * vetorMetodos.size() - 1) + 1;

        //insere nos outros metodos
        metodos.adicionarMetodo(vetorMetodos.get(indiceMetodo).toString());

        //pega o ultimo metodo inserido
        Metodo metodo = metodos.getLastMetodo();

        if (metodo.hasParametro()) {

            Referencias refs = new Referencias(metodo.extrairParametros());

            String[] novosParametros = new String[metodo.quantidadeParametros()];

            //para cada parametro
            for (int i = 0; i < metodo.quantidadeParametros(); i++) {
                //gera um bloco, mas não necessariamente vai ser válido
                //detalhes dentro do metodo
                Parametro novoParametro = refs.gerarBlocoPorTipo(refs.getRef(i));
                if (novoParametro != null) {
                    novosParametros[i] = novoParametro.toString();
                } else {
                    i--;
                }
            }
            parametros.adicionarParametros(novosParametros);
        }

        dt.setMetodos(metodos);
        dt.setParametros(parametros);

        return dt;
    }

    /**
     * algumas invocações de métodos (exceto a última)
     * são randomicamente selecionadas e removidas. Valores
     * ou objetos requeridos anteriormente são excluídos.
     *
     * @param dt in
     * @return dt out
     */
    public DadoTestePO removerInvocacaoMetodo(DadoTestePO dt) {

        Metodos metodos = dt.getMetodos();
        Parametros parametros = dt.getParametros();

        //se tiver parametros
        if (parametros.hasParameter()) {
            parametros = dt.getParametros();
        }

        //se tiver só um metodo, nao faz nada
        if (metodos.quantidade() <= Constantes.TAMANHO_MIN_DT) {
            return dt;
        }
        
        Metodo metodoEscolhido = metodos.sortearMetodo();

        if (metodoEscolhido.hasParametro()) {

            int iParametro = 0;
            //para cara metodo anterior
            for (int i = 0; i < metodoEscolhido.indice(); i++) {
                //verifica se tem parametro
                if (metodos.getMetodo(i).hasParametro()) {
                    //esse valor vai ser util quando for remover o parametro
                    //do metodo escolhido
                    iParametro = iParametro + metodos.getMetodo(i).quantidadeParametros();
                }
            }

            int quantidadeParametros = metodoEscolhido.quantidadeParametros();
            
            for (int i = 0; i < quantidadeParametros; i++) {
                int indiceRemocao = i + iParametro;

                if (indiceRemocao >= parametros.quantidade()) {
                    indiceRemocao = parametros.quantidade() - 1;
                }

                parametros.removerParametro(indiceRemocao);
            }
        }

        //rasga!
        metodos.removerMetodo(metodoEscolhido.indice());

        dt.setMetodos(metodos);
        dt.setParametros(parametros);

        return dt;
    }
}
